﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ATPlatforms.RabbitSkin.Serializers;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using RabbitMQ.Client.MessagePatterns;

namespace ATPlatforms.RabbitSkin
{
    public class RpcClient<T, TR> : BaseProducerConsumer
    {
        protected string MethodName { get; set; }
        private string RoutingKey { get; set; }
        public ISerializeMessages<T> RequestMessageSerializer { get; set; }
        public ISerializeMessages<TR> ResponseMessageSerializer { get; set; }
        private SimpleRpcClient mSyncClient;
        private AsyncSimpleRpcClient mAsyncClient;
        private Dictionary<string, OnSuccessHandler> mCallbacks = new Dictionary<string, OnSuccessHandler>();

        public delegate void OnSuccessHandler(TR response);

        protected string ClientId { get; set; }


        public RpcClient(string server, string methodName)
            : this(server, methodName, new BinaryMessageSerializer<T>(), new BinaryMessageSerializer<TR>())
        {

        }

        public RpcClient(string server, string methodName, 
            BinaryMessageSerializer<T> requestMessageSerializer, ISerializeMessages<TR> reponseMessageSerializer)
            : base(server, "", ExchangeType.Direct)
        {

            MethodName = methodName;
            //ClientId = clientId;
            RoutingKey = string.Format("{0}_{1}", MethodName, ClientId);
            RequestMessageSerializer = requestMessageSerializer;
            ResponseMessageSerializer = reponseMessageSerializer;
        }

        public override bool StartBasic()
        {
            if (base.StartBasic())
            {
                mSyncClient = new SimpleRpcClient(Model, MethodName);
                mAsyncClient = new AsyncSimpleRpcClient(Model, MethodName, new AsyncSimpleRpcClient.OnReply(OnReply));
                //create a queue binging in the format methodname_clientid  to route all traffic back
               // mQueue = Model.QueueDeclare();
                //Model.QueueBind(mQueue, Exchange, RoutingKey, false, null);
                return true;
            }
            return false;
        }




        public TR Call(T requestMessage)
        {
            
            // in the line above, the "..." indicates the parameters
            // used to specify the address to use to route messages
            // to the service.
            // The next three lines are optional:
            //client.TimeoutMilliseconds = 5000; // defaults to infinity
            /*client.TimedOut += new EventHandler(TimedOutHandler);
            client.Disconnected += new EventHandler(DisconnectedHandler);*/
            
            //IBasicProperties requestProps = Model.CreateBasicProperties();
            //IBasicProperties responseProps = Model.CreateBasicProperties();
            //responseProps.ReplyToAddress = new PublicationAddress(ExchangeType.Direct, Exchange, mQueue);

            byte[] replyMessageBytes = mSyncClient.Call(RequestMessageSerializer.SerializeMessage(requestMessage));
            return ResponseMessageSerializer.DeSerializeMessage(replyMessageBytes);
            // other useful overloads of Call() and Cast() are
            // available. See the code documentation of SimpleRpcClient
            // for full details.

        }

        public bool AsyncCall(T requestMessage, OnSuccessHandler onSuccessHandler )
        {
      
            // in the line above, the "..." indicates the parameters
            // used to specify the address to use to route messages
            // to the service.
            // The next three lines are optional:
            //client.TimeoutMilliseconds = 5000; // defaults to infinity
            /*client.TimedOut += new EventHandler(TimedOutHandler);
            client.Disconnected += new EventHandler(DisconnectedHandler);*/

            //IBasicProperties requestProps = Model.CreateBasicProperties();
            //IBasicProperties responseProps = Model.CreateBasicProperties();
            //responseProps.ReplyToAddress = new PublicationAddress(ExchangeType.Direct, Exchange, mQueue);
            try
            {
                string correlationId = mAsyncClient.AsyncCall(RequestMessageSerializer.SerializeMessage(requestMessage));
                mCallbacks.Add(correlationId, onSuccessHandler);
            }
            catch (Exception)
            {

                return false;
            }
           
            //return ReponseMessageSerializer.DeSerializeMessage(replyMessageBytes);
            // other useful overloads of Call() and Cast() are
            // available. See the code documentation of SimpleRpcClient
            // for full details.
            return true;
        }

        public void  OnReply(BasicDeliverEventArgs reply)
        {
            string correlationId = reply.BasicProperties.CorrelationId;
            if (mCallbacks.ContainsKey(correlationId))
            {
            
                try
                {
                    if (mCallbacks[correlationId] != null)
                    {
                        mCallbacks[correlationId].BeginInvoke(
                            ResponseMessageSerializer.DeSerializeMessage(reply.Body), null, null);
                        mCallbacks.Remove(correlationId);
                    }
                }
                catch (Exception e)
                {
                    
                    //do something or not???
                }
     
            }
        }
    }
}
